![]() |
![]() |
|
Die allgemeine Syntax zur Deklaration einer Variablen lautet wie folgt:
Variablen zu deklarieren heißt, einem Bezeichner mit der As-Klausel einen bestimmten Datentypen zuzuordnen. Die Angabe des Datentyps ist optional und hängt vom Optionsschalter Option Strict ab. Dieser Schalter kann, wie auch schon der Schalter Option Explicit, entweder im Projekteigenschaftsfenster gesetzt werden (siehe dazu auch die Abbildung 3.2) oder mit
außerhalb eines Moduls. Die Standardeinstellung ist Off. Dies hat zur Folge, dass die As-Klausel bei einer Variablendeklaration nicht angegeben werden muss:
Die Schalterstellung hat zudem einen entscheidenden Einfluss auf die Typkonvertierung, mit der wir uns weiter unten in diesem Kapitel noch beschäftigen werden. In einer Codezeile können durchaus mehrere Variablen deklariert werden. Außerdem kann eine Variable schon bei der Deklaration initialisiert, ihr also ein spezifischer Startwert zugewiesen werden.
Der VariablennameDer Variablenname (Bezeichner) kann nahezu beliebig festgelegt werden, unterliegt aber besonderen Reglementierungen:
Die letzte Regel lässt sich mit einem kleinen Trick umgehen. Wenn Sie beispielsweise – aus welchen Gründen auch immer – einer Variablen den Namen des von VB 2005 reservierten Schlüsselworts Public geben wollen, können Sie diesen Bezeichner in eckige Klammern setzen:
Mit der Klammerung wird die Beziehung außer Kraft gesetzt. Inwieweit dies von praktischem Nutzen ist, mögen Sie selbst entscheiden. Zur Verdeutlichung dieser Regeln einige Beispiele:
VB 2005 unterscheidet nicht zwischen der Groß- und Kleinschreibung wie die meisten anderen Programmiersprachen. Dennoch wird der Groß-/Kleinschreibung im Texteditor Rechnung getragen. Ausschlaggebend ist die Angabe in der Deklaration. Sie können sich dies sogar zunutze machen, wenn Sie bei der Deklaration einer Variablen dem Bezeichner einen oder mehrere Großbuchstaben mit auf den Lebensweg geben, z. B.:
Weisen Sie im Laufe des weiteren Programmierens der Variablen einen Wert zu und schreiben den Namen dabei nur in Kleinbuchstaben, beispielsweise
wird die Entwicklungsumgebung beim Zeilenumbruch automatisch die deklarierte Schreibweise erkennen und entsprechend in
korrigieren. Mit dieser Technik können Sie sofort erkennen, ob Sie den Namen der Variablen richtig geschrieben haben, und mögliche lästige Fehlermeldungen beim späteren Testen vermeiden. Noch ein Hinweis zur Namensvergabe: Wählen Sie grundsätzlich beschreibende Namen, damit Ihr Code später besser lesbar wird. Einfache Bezeichner wie x oder y usw. sind wenig aussagefähig. Besser wäre eine Wahl wie intFarbe, dblGehalt, strVorname usw. Nur den Zählervariablen von Schleifen werden meistens Kurznamen gegeben. Empfehlenswerterweise sollten die Bezeichner von Variablen mit einem Kleinbuchstaben anfangen – im Gegensatz zu den Namen von Prozeduren und Objekten. Sie können dazu ein Präfix benutzen, aus dem auch innerhalb einer Anweisung hervorgeht, welcher Datentyp von der Variablen beschrieben wird:
Die Verwendung eines Präfixes zusammen mit einem sinnvollen, beschreibenden Bezeichner macht den Programmcode lesbarer. Zusammen mit einer sauberen Strukturierung durch Einzüge und mit ausreichenden Kommentaren versehen wird auch eine dritte Person kaum Schwierigkeiten haben, in angemessener Zeit die Programmlogik zu interpretieren. Die folgende Tabelle 3.1 enthält die Präfixe für die verschiedenen Datentypen von VB 2005, die als Vorschläge gedacht sind.
Der DatentypMit dem Datentyp wird festgelegt, wie groß der Wertebereich der zu speichernden Dateninformation sein soll, wie viel Speicherplatz dafür reserviert werden muss und, letztendlich damit auch, wie dieser Speicherplatz zur Laufzeit interpretiert wird. Weiter unten in Abschnitt 3.2.3 werden alle zur Verfügung stehenden Datentypen erläutert.
In diesem Kapitel werden Sie nur die nativen Datentypen kennen lernen, mit den anderen werden wir uns ab Kapitel 4 auseinander setzen. Eine weitere Gruppierung der Datentypen erfolgt nach:
Werte- bzw. Referenztypen beschreiben, wie die Daten im Speicher vorgehalten werden und wie mit ihnen gearbeitet wird. Die meisten nativen Datentypen gelten unter .NET als Wertetypen, die auf Klassendefinitionen basierenden Objekte als Referenztypen. Die Ausnahmen sind die Typen Object und String, die zwar den nativen Datentypen zugerechnet werden können, ihrem Wesen nach aber Referenztypen sind. Obwohl die Begriffe Objekt, Referenz- und Wertetyp in den folgenden Abschnitten noch öfters fallen werden, wird die genaue Erklärung erst in Kapitel 4 erfolgen, wenn wir uns intensiv mit Klassen und Objekten beschäftigen. Die ZugriffsmodifiziererDie Schlüsselwörter Dim, Static, Public und Private beschreiben die Sichtbarkeit und Lebensdauer einer Variablen und werden als Zugriffsmodifizierer bezeichnet. Sie können bei der Deklaration nur einen Zugriffsmodifizierer angeben. Über die Bedeutung der Einzelnen werden Sie weiter unten in diesem Abschnitt alles Weitere erfahren. 3.2.3 Die einfachen Datentypen
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .NET-Laufzeittyp | VB -Alias | CLS-kompatibel | Wertebereich |
| Byte | Byte | ja | 0 bis 255 |
| SByte | SByte | nein | –128 ... 127 |
| Int16 | Short | ja | –215 . 215 – 1 |
| UInt16 | UShort | nein | 0 . 65535 |
| Int32 | Integer | ja | –231 . 231 – 1 |
| UInteger | nein | 0 ... 232 – 1 | |
| Int64 | Long | ja | –263 . 263 – 1 |
| UInt64 | ULong | nein | 0 . 264 – 1 |
| Single | Single | ja | 1,4*10 – 45 bis 3,4*1038 |
| Double | Double | ja | 5,0*10 – 324 bis 1,7*10308 |
| Decimal | Decimal | ja | +/–79E27 ohne Dezimalpunktangabe; +/–7.9E-29, falls 28 Stellen hinter dem Dezimalpunkt angegeben werden. Die kleinste darstellbare Zahl beträgt +/–1.0E-29 |
| Char | Char | ja | Unicode-Zeichen zwischen 0 und 65535 |
| String | String | ja | ca. 231 Unicode-Zeichen |
| Boolean | Boolean | ja | True oder False |
| Object | Object | ja | Eine Variable vom Typ Object kann jeden anderen Datentypen enthalten, ist also universell. |
In der ersten Spalte ist der Typbezeichner in der .NET-Klassenbibliothek angeführt, in der zweiten Spalte der VB-Alias, der bei der Deklaration einer Variablen dieses Typs angegeben werden kann.
Zu den Angaben in der dritten Spalte muss ich Ihnen eine Erklärung geben. .NET verfolgt nicht nur ein plattformunabhängiges Konzept, sondern auch ein sprachunabhängiges. Das bedeutet, dass eine Komponente, die in einer fiktiven .NET-Programmiersprache A geschrieben wird, auch den vollen Zugriff auf alle Features einer Komponente haben sollte, die in einer anderen .NET-Sprache, nennen wir sie hier der Einfachheit halber B, implementiert ist.
Das kann nur problemlos funktionieren, wenn sich beide auf einen gemeinsamen Nenner hinsichtlich der Sprachfeatures geeignet haben, zu denen auch die elementaren Datentypen zu rechnen sind. Dieser gemeinsame Nenner wird durch die Common Language Specification (CLS) vorgegeben. Das bedeutet, wenn eine .NET-Anwendung CLS-konform codiert wird, ist eine Garantie damit verbunden, dass jeder andere .NET-Code Zugriff auf die (öffentlichen) Komponenten der CLS-konformen Anwendung hat – unabhängig davon, in welcher Sprache sie codiert ist.
Wie Sie sehen, sind nicht alle Datentypen der Tabelle 3.2 CLS-konform. Sie können diese zwar innerhalb Ihrer Anwendung problemlos einsetzen, aber in der öffentlichen Schnittstelle haben sie nichts zu suchen.
Wie der Tabelle zu entnehmen ist, basieren alle Typen auf einer Klassendefinition im .NET-Framework. Das hat zur Folge, dass anstelle der Angabe des C#-Alias zur Typbeschreibung auch der .NET-Laufzeittyp genannt werden kann. Damit sind die beiden folgenden Deklarationen der Variablen intVar absolut gleichwertig:
| Dim intVar As Integer |
| Dim intVar As Int32 |
VB 2005 stellt acht ganzzahlige Datentypen zur Verfügung, von denen vier vorzeichenbehaftet sind, der Rest nicht. Die uns interessierenden CLS-konformen sind
| Byte |
| Int16 |
| Int32 |
| Int64 |
Int16, Int32 und Int64 haben einen Wertebereich, der nahezu gleichmäßig über die negative und positive Skala verteilt ist. Die vorzeichenlosen Datentypen, zu denen auch Byte gehört, decken hingegen nur den positiven Wertebereich, beginnend bei 0, ab. Der vorzeichenlose Typ Byte, der im Gegensatz zu SByte CLS-konform ist, ist insbesondere dann von Interesse, wenn auf binäre Daten zugegriffen wird.
Ganzzahlige Literale können in Dezimal-, Hexadezimal- oder Oktalform übergeben werden. Hexadezimale Zahlen (Basis = 16) erhalten das Präfix &H, oktale (Basis = 8) das Präfix &O. Die folgenden Zahlen beschreiben beide dieselbe Dezimalzahl, nämlich 225, zuerst in hexadezimaler, danach in oktaler Schreibweise:
| Dim intHex As Integer = &HE1 |
| Dim intOct As Integer = &O341 |
Single, Double und Decimal sind die drei Typen, mit denen unter Visual Basic Fließkommazahlen dargestellt werden können. Sie beschreiben nicht nur unterschiedliche Wertebereiche, sondern auch – was noch viel wichtiger ist – unterschiedliche Genauigkeiten. Auf herkömmlichen Systemen beträgt die Genauigkeit eines Single-Typs etwa zehn Stellen, der eines Double-Typs etwa 16 Stellen. Abhängig ist die Genauigkeit dabei immer von der Anzahl der Ziffern des ganzzahligen Anteils der Dezimalzahl.
Das folgende Codefragment demonstriert die Genauigkeit, die mit einem Single erreicht werden kann.
| ' -------------------------------------------------------- |
| ' Beispiel: ...\Kapitel 3\DezGenauigkeit |
| ' -------------------------------------------------------- |
| Module Module1 |
| Sub Main() |
| Dim x, y As Single |
| x = 0.123456789 |
| y = 0.1234567891 |
| If x = y Then |
| Console.WriteLine("Beide Werte sind gleich.") |
| Else |
| Console.WriteLine("Beide Werte sind ungleich.") |
| End If |
| Console.ReadLine() |
| End Sub |
| End Module |
Es werden zunächst zwei Variablen vom Typ Single deklariert und beiden danach ein Wert zugewiesen, der sich nur an der zehnten Nachkommastelle unterscheidet. Mit der Anweisung If...End If werden die Inhalte beider Variablen auf Gleichheit überprüft. Entspricht der von x repräsentierte Wert dem von y, soll die Meldung »Beide Werte sind gleich« ausgegeben werden, weichen die Variableninhalte voneinander ab, sollte die Ausgabe »Beide Werte sind ungleich« lauten.
Erstaunlicherweise erscheint nach dem Start der Laufzeit die Meldung mit der Aussage, die von den beiden Variablen beschriebenen Werte seien gleich. Diese offensichtliche Falschaussage ist darauf zurückzuführen, dass ein Single-Typ nicht in der Lage ist, alle im vorliegenden Fall angegebenen Nachkommastellen exakt zu interpretieren – er ist schlichtweg überfordert. Für Berechnungen, die eine höhere Genauigkeit erfordern, ist dieser Datentyp daher weniger gut geeignet.
Ein ähnlicher Test – diesmal mit einem Double – führt zu demselben Ergebnis. Allerdings kann die Anzahl der Nachkommastellen unter Beibehaltung der Genauigkeit erhöht werden. Dies ist auch meistens der Grund dafür ist, sich für den einen oder anderen Datentypen zu entscheiden – die Größenordnung, die ein Single darstellen kann, ist per Definition bereits so groß, dass hier weniger das Entscheidungskriterium zu suchen ist, die Frage ist nur: Reicht die Genauigkeit für die Berechnung aus?
Wenn von der Genauigkeit von Fließkommazahlen gesprochen wird, bedeutet das nicht, dass die Genauigkeit auf den Dezimalteil bezogen wird. Vielmehr ist es die Genauigkeit, mit der beispielsweise beim Single die ersten zehn Zahlen – beginnend links mit der ersten – unterschieden werden können. Um dies zu testen, brauchen Sie nur die beiden Literale des vorhergehenden Beispiels zu ändern, z. B.:
| Sub Main() |
| Dim x, y As Single |
| x = 10.123456 |
| y = 10.1234567891 |
| If x = y Then |
| Console.WriteLine("Beide Werte sind gleich.") |
| Else |
| Console.WriteLine("Beide Werte sind ungleich.") |
| End If |
| Console.ReadLine() |
| End Sub |
Wenn Sie diesen Code laufen lassen, werden Sie die folgende Ausgabe erhalten:
| Beide Werte sind ungleich. |
Hängen Sie die Zahl 7 als letzte Nachkommastelle an die Variable x an, also
| x = 10.1234567 |
sieht der Compiler beide Variableninhalt als gleich an. Er berücksichtigt demnach die achte Nachkommastelle nicht mehr für den Vergleich.
Der Forderung nach sehr hoher Genauigkeit einer Dezimalzahl können die beiden Datentypen Single und Double oft nicht ausreichend erfüllen. Bei höheren Ansprüchen muss die Wahl auf einen anderen, passenderen Datentypen fallen: Decimal. Decimal kann Zahlen darstellen, die bis zu 28 Nachkommastellen aufweisen. Je mehr Nachkommastellen eine Zahl hat, desto höher ist auch die Genauigkeit, mit der Operationen ausgeführt werden können. Mit steigender Genauigkeit verringert sich andererseits jedoch auch der darstellbare Wertebereich.
Während die Zuweisung eines ganzzahligen Literals an ein Decimal in bekannter Art und Weise erfolgt, also beispielsweise mit
| Dim decA As Decimal |
| decA = 120 |
muss bei der Zuweisung einer numerischen Zahl mit großem Dezimalanteil ein kleiner Trick angewandt werden. Wenn Sie im Texteditor beispielsweise
| decA = 0.1234567890123456789012 |
eingeben, wird ihre Eingabe nach dem Zeilenumbruch in
| decA = 0.123456789012346 |
eingekürzt. Im ersten Moment erscheint dies unverständlich und wenig plausibel, die Erklärung ist allerdings recht einfach. Zur Laufzeit der Anwendung muss einem Literal wie jedem anderen Datum auch ein Speicherplatz zugewiesen werden. Dabei wird
| ein ganzzahliges Literal als Integer interpretiert, |
| ein Literal vom Typ einer Dezimalzahl als Double. |
Nach den Gesetzen der Typkonvertierung, die wir in Abschnitt 3.2.6 behandeln werden, kann einer Zahl vom Typ Integer einem Decimal-Wert zugewiesen werden, ohne dass es zu einem Datenverlust kommt. Der Integer wird implizit in Decimal konvertiert. Dieselbe Aussage gilt allerdings nicht, wenn ein Double einem Decimal zugewiesen werden soll – es kommt zu einer Fehlermeldung in der Entwicklungsumgebung. Dieses Verhalten kann durch das Anhängen des Buchstabens »D« vermieden werden:
| decA = 0.1234567890123456789012D |
Dieses Verhalten gilt nur, wenn der Schalter Option Strict On gesetzt ist.
Betrachten wir nun zum Abschluss noch einmal beispielhaft die Aufteilung der darstellbaren 28 Zahlen eines Decimal-Typen in den ganzzahligen und dezimalen Anteil.
| decA = 0.1234567890123456789012345678D |
decA wird ein Literal mit 28 Nachkommastellen zugewiesen. Geben Sie eine noch genauere Zahl an, wird der unakzeptable Teil von der Entwicklungsumgebung automatisch abgeschnitten.
Wir wollen nun die Genauigkeit testen, mit welcher der Datentyp Decimal zu arbeiten in der Lage ist. Dazu dient uns der folgende Code:
| ' --------------------------------------------------------------- |
| ' Beispiel: ...\Kapitel 3\DecimalVergleich |
| ' --------------------------------------------------------------- |
| Module Module1 |
| Sub Main() |
| Dim x, y, z As Decimal |
| x = 0.1234567890123456789012345678D |
| y = 0.1234567890123456789012345678D |
| z = 0.1234567890123456789012345679D |
| 'Vergleich der beiden Variablen x und y |
| If x = y Then |
| Console.WriteLine("x und y sind gleich.") |
| Else |
| Console.WriteLine("x und y sind ungleich.") |
| End If |
| 'Vergleich der beiden Variablen x und z |
| If x = z Then |
| Console.WriteLine("x und z sind gleich.") |
| Else |
| Console.WriteLine("x und z sind ungleich.") |
| End If |
| Console.ReadLine() |
| End Sub |
| End Module |
Beachten Sie, dass die beiden Variablen x und y identisch sind, während sich x und z in der 28. Nachkommastelle unterscheiden. Lassen Sie das Programm laufen, wird an der Konsole folgende Ausgabe angezeigt.
| x und y sind gleich. |
| x und z sind ungleich. |
Die Laufzeit erkennt den Unterschied an der 28. Nachkommastelle, was uns das Ergebnis der Anzeige beweist. Das ist natürlich eine deutliche Steigerung gegenüber den beiden anderen Typen Single und Double.
Wird der ganzzahlige Anteil eines Decimals vergrößert, z. B.:
| decA = 1000.1234567890123456789012346D |
bewirkt jede weitere Ziffer links vom Komma einen Verlust an Genauigkeit, im Beispiel von decA weist der Dezimalteil nur noch eine Genauigkeit von 25 Stellen rechts vom Komma auf.
Genau genommen wird die letzte Ziffer einer Fließkommazahl eines Decimal-Typs schon dann abgeschnitten, wenn der ganzzahlige Anteil größer 7 ist.
| Hinweis |
|
Beachten Sie, dass der ganzzahlige Anteil einer Fließkommazahl grundsätzlich immer durch einen Punkt vom Nachkommaanteil getrennt wird. Diese Regel gilt auch dann, wenn Sie in der spezifischen Ländereinstellung auf Systemebene ein Komma als Trennzeichen eingestellt haben. |
Variablen vom Typ Char können ein Zeichen des Unicode-Zeichensatzes aufnehmen. Unicode ist die Erweiterung des 1 Byte großen ASCII- bzw. ANSI-Zeichensatzes mit seinen insgesamt 256 verschiedenen Zeichen. Unicode berücksichtigt die Bedürfnisse außereuropäischer Zeichensätze – z. B. das japanische Kanji –, für die eine 1-Byte-Codierung nicht ausreichend ist. Jedes Unicode-Zeichen beansprucht 2 Byte, folglich ist der Unicode-Zeichensatz auch auf 65536 Zeichen beschränkt. Die ersten 128 Zeichen (0 – 127) entsprechen denen des ANSI-Zeichensatzes, die folgenden 128 Zeichen beinhalten unter anderem Sonderzeichen und Währungssymbole.
| Dim chrZeichen As Char = "A" |
Das funktioniert allerdings nur, wenn Option Strict ausgeschaltet ist (Off). Für den Fall, dass Sie diesen Schalter gesetzt haben (On), muss die Zuweisung eines Zeichens an die Char-Variable umgewandelt werden. Das können Sie entweder über die Konvertierungsfunktion CChar oder über das Anhängen des Typkennzeichens C erreichen.
| Dim c As Char |
| 'Anhängen des Typkennzeichens C |
| c = "A"C |
| 'Konvertierungsfunktion CChar |
| c = CChar("A") |
Versuchen Sie, einer Char-Variablen mehr als ein Zeichen zuzuweisen, ist nur das erste ausschlaggebend.
Der Typ Char beschränkt sich nur auf ein Zeichen. Um eine Zeichenkette, die sich aus keinem oder bis zu maximal ungefähr 231 Einzelzeichen zusammensetzt, zu speichern oder zu bearbeiten, deklarieren Sie eine Variable vom Datentyp String. Die Einzelzeichen werden dabei wie ein Char-Typ als Unicode-Zeichen der Größe 16 Bit behandelt. Zeichenketten werden grundsätzlich in Anführungsstriche gesetzt.
| Dim str As String = "VB.NET macht Spass!" |
Die Länge einer Stringvariablen ist dynamisch und passt sich der Länge der zugewiesenen Zeichenfolge an.
Variablen vom Typ Boolean können nur zwei Zustände beschreiben, nämlich True oder False.
In vielen Programmiersprachen wird False numerisch mit 0 beschrieben, True durch alle Werte, die von 0 abweichen. .NET ist hier sehr viel strenger. Hier ist True nicht 1 und auch nicht 67, sondern ganz schlicht True. Aus diesem Grund ist auch die folgende Anweisung falsch, die so in anderen Programmiersprachen durchaus möglich ist:
| Dim myBool As Boolean = 2 |
Das hat natürlich auch Auswirkungen auf Bedingungsprüfungen, wie Sie später noch sehen werden.
Dieser allgemeinste aller Datentypen, Object, beschreibt in seinen vier Byte einen Zeiger auf die Adresse eines Objekts der aktuellen oder einer anderen Anwendung. Eine Variable dieses Typs kann jeden beliebigen anderen Datentypen beschreiben: Ob es sich um einen Integer, einen String, um eine Datenbankverbindung oder um ein anderes spezielles Objekt wie zum Beispiel um eine Schaltfläche handelt, spielt dabei keine Rolle. Zur Laufzeit wird eine auf dem Typ Object basierende Variable passend aufgelöst und die gewünschte Operation darauf ausgeführt. Im folgenden Codefragment wird beispielsweise eine Variable des Typs Object deklariert und anschließend mit einer Ganzzahl initialisiert:
| Dim x As Object |
| x = 3400 |
Ganz gleich, was eine Variable vom Typ Object beinhaltet, sie enthält grundsätzlich immer nur einen Zeiger (Verweis oder auch Referenz) auf ein Objekt – selbst dann, wenn es sich um eine Zahl handelt, denn die .NET-Laufzeitumgebung kapselt jedes Literal in einem Objekt. Herkömmliche Betriebssysteme beschreiben 32-Bit-Speicheradressen. Daher ergibt sich auch die einheitliche Größe dieses Datentyps von 4 Byte – unabhängig davon, welcher Typ referenziert wird.
Eine Variable zu deklarieren, sieht so harmlos und unscheinbar aus. Und dennoch, hinter dem Variablennamen verbergen sich Möglichkeiten, die Sie bisher noch nicht ansatzweise erahnt haben. In .NET wird alles durch die objektorientierte Brille betrachtet, sogar die nativen Datentypen.
Ein einfacher Integer soll ein Objekt sein? Wenn Sie dieser Aussage keinen Glauben schenken wollen, dann deklarieren Sie eine Variable vom Typ Integer:
| Dim intVariable As Integer |
Schreiben Sie dann in der folgenden Zeile:
| intVariable. |
Beachten Sie, dass hinter dem Variablennamen ein Punkt gesetzt ist. Mit Erstaunen werden Sie feststellen, dass hinter der Punktangabe plötzlich eine Liste aufgeklappt wird – die IntelliSense-Unterstützung (siehe Abbildung 3.4).
In dieser Liste sind alle Eigenschaften und Methoden aufgeführt, die ein Objekt vom Typ Integer auszeichnen. Sie können sich dieser Funktionalitäten bedienen. Wenn Sie beispielsweise wissen wollen, wo die Ober- bzw. Untergrenze des Integers liegt, könnten Sie dies mit dem folgenden Codefragment abfragen:
| Sub Main() |
| Dim intVariable As Integer |
| Console.WriteLine("Integer(min) = {0}", intVariable.MinValue) |
| Console.WriteLine("Integer(max) = {0}", intVariable.MaxValue) |
| Console.ReadLine() |
| End Sub |

Hier klicken, um das Bild zu Vergrößern
Abbildung 3.4 IntelliSense-Liste einer Integervariablen
Beachten Sie bitte hierbei, dass in syntaktisch gleicher Weise, wie Sie die Methode WriteLine der Klasse Console mittels Punktnotation aufrufen, auch auf die Ober- und Untergrenze des Typs Integer zugreifen: zuerst die Angabe des Objektnamens, der in unserem Beispiel intVariable lautet, danach durch einen Punkt abgetrennt die Methode bzw. Eigenschaft (in unserem Fall handelt es sich um eine Eigenschaft).
An der Konsole erfolgt danach die Anzeige:
| Integer(min) = –2147483648 |
| Integer(max) = 2147483647 |
Jede Variable unterliegt einer bestimmten Sichtbarkeit und Lebensdauer. Unter Sichtbarkeit wird verstanden, von welchen Stellen des Programmcodes aus auf eine Variable zugegriffen werden kann. Die Lebensdauer beschreibt, wie lange eine Variable den für sie reservierten Speicherplatz beansprucht und den Inhalt beibehält. Gesteuert werden Sichtbarkeit und Lebensdauer durch die Wahl eines passenden Modifizierers (Dim, Static, Public oder Private) sowie durch die Position der Deklaration.
In diesem Kapitel, das zunächst nur in die grundlegende Sprachsyntax einführt, orientieren wir alle Aussagen und Beispiele nur an Modulen. .NET kennt noch eine weitere, wesentlich wichtigere Codeeinheit: die Klasse. In einer Klasse wird eine Variable anders interpretiert, ihr kommt eine besondere Bedeutung zu. Außerdem müssen in Klassen noch weitere Zugriffsmodifizierer berücksichtigt werden, die bei den herkömmlichen Modulen keine Bedeutung haben. Diese Thematik werden wir im Moment unberücksichtigt lassen und erst in den folgenden beiden Kapiteln beleuchten.
Grundsätzlich können Variablen
| innerhalb einer Prozedur eines Moduls |
| im Allgemeinteil des Moduls |
deklariert werden. Im folgenden Codefragment soll dies verdeutlicht werden.
| Module Module1 |
| 'Variable im Deklarationsbereich des Moduls |
| Private intVar As Integer |
| Sub Main() |
| 'Variable innerhalb der Prozedur Main |
| Dim lngDate As Long |
| End Sub |
| Sub UserDefinedProc() |
| 'Variable innerhalb der Prozedur UserDefinedProc |
| Dim strValue As String |
| End Sub |
| End Module |
Die Variable intVar ist außerhalb jeglicher Prozedur deklariert – sie befindet sich im Deklarationsabschnitt des Moduls. Ihr Inhalt kann sowohl aus der Prozedur Main heraus gelesen oder verändert werden als auch aus der benutzerdefinierten Prozedur UserDefinedProc. Variablen im Deklarationsabschnitt eines Moduls werden als globale Variablen bezeichnet, weil sie sich (zumindest) im Sichtbarkeitsbereich aller Prozeduren des aktuellen Moduls befinden.
Sowohl innerhalb der Prozedur Main als auch innerhalb der Prozedur UserDefinedProc ist jeweils eine Variable deklariert. Variablen, die innerhalb einer Prozedur deklariert werden, bezeichnet man als lokale Variablen.
Eine Variable, die Sie innerhalb einer Prozedur deklarieren, wird als lokale Variable bezeichnet. Zulässige Zugriffsmodifizierer lokaler Variablen sind nur Dim und Static.
Lokale Variablen zeichnen sich dadurch aus, dass sie nur innerhalb der deklarierenden Prozedur bekannt sind – ihre Sichtbarkeit ist auf diese Prozedur beschränkt. Aus anderen Prozeduren heraus kann weder lesend noch schreibend auf den Inhalt einer lokalen Variablen zugegriffen werden. Außerdem ist durch die Dauer des Prozeduraufrufs auch die Lebensdauer dieser Variablen festgeschrieben. Nach dem Verlassen der Prozedur mit End Sub werden lokale Variablen zerstört und der von ihnen reservierte Speicher wieder freigegeben.
Es gibt jedoch Situationen, da ist Verlust des Inhalts nicht wünschenswert. Angenommen Sie möchten feststellen, wie oft die Prozedur UserDefinedProc während der Laufzeit des Programms ausgeführt wird. Dazu ist ein Zähler notwendig, der bei jedem Prozeduraufruf entsprechend erhöht wird. Eine lokale Variable, die mit dem Modifizierer Dim deklariert ist, kann diese Aufgabe nicht erfüllen, da sie ihren Inhalt mit Beendigung des Prozeduraufrufs verliert. Die Lösung wird durch den Modifizierer Static geboten. Die Inhalte statischer Variablen gehen nach dem Verlassen einer Prozedur nicht verloren, sondern bleiben erhalten. An der Sichtbarkeit ändert sich im Vergleich zu einer mit Dim deklarierten lokalen Variablen nichts, nur die Lebensdauer verlängert sich auf unbestimmte Zeit (genauer gesagt, auf die Laufzeit der Anwendung).
Um das Verhalten einer statischen Variablen zu demonstrieren, enthält das folgende Codefragment zusätzlich zu der Prozedur Main eine weitere, benutzerdefinierte Prozedur namens UserDefinedProc. Aus Main heraus wird die Prozedur UserDefinedProc dreimal aufgerufen. Bei jedem Aufruf wird innerhalb der aufgerufenen Prozedur die statische Variable intCounter um 1 erhöht.
| ' ------------------------------------------------------- |
| ' Beispiel: ...\Kapitel 3\StaticVariable |
| ' ------------------------------------------------------- |
| Module Module1 |
| Sub Main() |
| 'Aufrufe der benutzerdefinierten Prozedur |
| UserDefinedProc() |
| UserDefinedProc() |
| UserDefinedProc() |
| Console.Read() |
| End Sub |
| Sub UserDefinedProc() |
| 'statischer Zugriffszähler |
| Static intCounter As Integer |
| intCounter = intCounter + 1 |
| Console.WriteLine("{0}.Prozeduraufruf", intCounter) |
| End Sub |
| End Module |
Die Ausgabe an der Konsole erfolgt durch eine Anweisung in der benutzerdefinierten Prozedur. Dabei wird der Platzhalter {0} durch den aktuellen Stand der statischen Variablen intCounter ersetzt. Die Ausgabe lautet nach dem Start der Laufzeit:
| 1.Prozeduraufruf |
| 2.Prozeduraufruf |
| 3.Prozeduraufruf |
Würden wir den Modifizierer Static durch Dim ersetzen, würde uns dreimal hintereinander der erste Prozeduraufruf vorgegaukelt.
Außerhalb jeglicher Prozedur im Deklarationsteil eines Moduls definierte Variablen werden als global bezeichnet. Die Sichtbarkeit globaler Variablen wird über die Zugriffsmodifizierer gesteuert:
| Wird eine globale Variable Private deklariert (oder Dim, was ebenfalls zulässig ist und dieselbe Wirkung hat), kann sie von jeder Prozedur aufgerufen und manipuliert werden, die sich innerhalb des Moduls befindet. Außerhalb des Moduls ist die Variable nicht bekannt. Die Sichtbarkeit ist somit auf das Modul beschränkt. |
| Wird eine globale Variable Public deklariert, ist sie anwendungsweit bekannt. Der Wert kann von jeder Prozedur gelesen und geändert werden. Dabei spielt es keine Rolle, in welchem Modul sich die Prozedur befindet, aus der heraus der Zugriff erfolgt. |
Die lokale, statische Variable intCounter des Projektbeispiels StaticVariable könnte demnach auch durch eine globale ersetzt werden und würde in derselben Weise die an sie gestellten Anforderungen erfüllen. Anders läge der Sachverhalt, würde die Konsolenausgabe aus der Main-Prozedur heraus erfolgen. Dann könnte nur eine globale Zählervariable die an sie gestellte Aufgabe erfüllen – keine weitere Änderung des Programmcodes vorausgesetzt, wie zum Beispiel die Rückgabe des Zählerstands an die aufrufende Prozedur, was auch noch im Bereich der programmiertechnischen Möglichkeiten liegt.
| ' ------------------------------------------------------- |
| ' Beispiel: ...\Kapitel 3\GlobaleVariable |
| ' ------------------------------------------------------- |
| Module Module1 |
| Dim intCounter As Integer |
| Sub Main() |
| UserDefinedProc() |
| Console.WriteLine("{0}.Prozeduraufruf", intCounter) |
| UserDefinedProc() |
| Console.WriteLine("{0}.Prozeduraufruf", intCounter) |
| UserDefinedProc() |
| Console.WriteLine("{0}.Prozeduraufruf", intCounter) |
| Console.Read() |
| End Sub |
| Sub UserDefinedProc() |
| intCounter = intCounter + 1 |
| End Sub |
| End Module |
Es gilt als guter Programmierstil, einer Variablen nur die Sichtbarkeit zuzugestehen, die sie im Code auch tatsächlich benötigt. Dahinter verbirgt sich der Gedanke, eine nicht zulässige, unkontrollierte Manipulation auszuschließen. Außerdem gilt es auch zu bedenken, dass globale Variablen dauerhaft Speicherressourcen verbrauchen, was trotz der guten Ausstattung moderner Rechner in großen Anwendungen durchaus ein gewichtiges Argument sein kann.
Bezeichner von Variablen müssen grundsätzlich in ihrem direkten Definitionsbereich eindeutig sein. Innerhalb einer Prozedur dürfen beispielsweise nicht zwei gleichnamige Variablen deklariert werden. Dennoch ist es möglich, zwei Variablen mit gleichem Namen zu deklarieren, deren Sichtbarkeitsbereich sich überschneidet. Betrachten Sie dazu das folgende Codefragment:
| Module Module1 |
| Public intVar As Integer |
| Sub Main() |
| Dim intVar As Integer |
| ... |
| End Sub |
| End Module |
Auf Modulebene wird die Variable intVar deklariert, eine gleichnamige Variable in der Prozedur Main. Aus der Position der Prozedur heraus sind beide Variablen gleichermaßen sichtbar. Dennoch verursachen sie keinen Mehrdeutigkeitskonflikt, da sie sich in ihrem individuellen Gültigkeitsbereich unterscheiden. Die unabdingbare Eindeutigkeit eines Variablennamens ist somit auch in diesem Fall gewährleistet.
Wenn zwei Variablen denselben Namen, aber einen unterschiedlichen Gültigkeitsbereich haben, wird bei Angabe des Bezeichners auf die Variable zugegriffen, die den kleineren Gültigkeitsbereich aufweist – es ist die lokale. Die lokal deklarierte Variable überdeckt die globale. Soll nun aus der Prozedur heraus, welche die lokale Variante der Variable bereitstellt, auf die gleichnamige globale zugriffen werden, muss der qualifizierende Name der Variablen angegeben werden, beispielsweise:
| Module1.intVar |
Bei einem qualifizierenden Namen wird vor dem Variablennamen zusätzlich der Name des Moduls angegeben, in dem die globale Variable deklariert ist. Modulname und Variablenname werden durch einen Punkt voneinander getrennt (Punktnotation). Allgemein gültig formuliert sieht die Syntax folgendermaßen aus:
| <Modulname>.<Variablenbezeichner> |
Nach der Deklaration werden Variablen mit Werten initialisiert, die dem Datentyp entsprechen. Die Initialisierung kann explizit in der Deklarationsanweisung erfolgen, beispielsweise mit:
| Public intNewVar As Integer = 9 |
| Dim strMeldung As String = "Visual Studio" |
Wird bei der Deklaration einer Variablen nicht explizit ein Datum zugewiesen, wird sie vor-initialisiert. Die folgende Tabelle enthält die Vorinitialisierungswerte der verschiedenen Datentypen.
| Datentypen | Vorinitialisierung |
| Short, Integer, Long, Single, Double, Decimal, Byte | 0 |
| String | Leerstring |
| Boolean | False |
| Object | Nothing |
Beachten Sie bitte, dass die Vorinitialisierung unabhängig davon ist, ob es sich um eine lokale oder um eine globale Variable handelt.
In Visual Basic 2005 können in einem Ausdruck unterschiedliche native Datentypen eingesetzt werden. Das bedeutet, dass die an einer Operation beteiligten Operanden einer Anweisung unterschiedliche Datentypen aufweisen können. In diesem Fall muss vom Compiler eine Typanpassung vorgenommen werden, bei der die beiden Operanden einen gemeinsamen Datentyp für die Operation erhalten. Eine solche Typanpassung wird als Datentypkonvertierung bezeichnet. Dabei werden zwei Arten unterschieden:
| Die implizite Konvertierung, bei welcher der kleinere der beiden beteiligten Datentypen in den größeren umgewandelt wird. |
| Die explizite Konvertierung, bei welcher der größere der beiden Datentypen in den Typ des kleineren überführt wird. |
Bei allen Konvertierungen bleibt das Original unverändert.
Betrachten Sie zunächst die dritte Zeile des folgenden Codefragments, in welcher der Inhalt einer Variablen vom Typ Integer einer zweiten Variablen, diesmal vom Typ Long, zugewiesen werden soll.
| Dim intVar As Integer = 20 |
| Dim lngVar As Long |
| lngVar = intVar |
Um der Variablen vom Typ Long einen Integer-Wert zuzuweisen, ist eine Typumwandlung notwendig, die automatisch vom Compiler durchgeführt und daher als implizite Konvertierung bezeichnet wird. Implizite Konvertierungen setzen immer eine Typaufweitung voraus – der Inhalt eines Integers wird in einen Long gesteckt, der definitionsgemäß einen viel größeren Wertebereich abdeckt.
|
Bei der impliziten Konvertierung – d. h. Option Strict On – bleibt die Größe eines Werts grundsätzlich immer erhalten. Die Genauigkeit kann allerdings durchaus Einbußen erleiden. |
Die wesentlichsten impliziten Konvertierungen primitiver Datentypen sind der folgenden Abbildung zu entnehmen. Die Pfeilrichtung gibt dabei die Richtung der automatischen Konvertierung vor. Ein Integer darf demzufolge in einen Long, Decimal, Single oder Double umgewandelt werden, ein Single nur in einen Double.

Hier klicken, um das Bild zu Vergrößern
Abbildung 3.5 Implizite Datentypkonvertierung
Eine besondere Stellung nehmen die Datentypen Char, String und Boolean ein. Ein Char-Typ kann implizit nur in einen Integer konvertiert werden, mit einem String bzw. einem Boolean sind keine automatischen Konvertierungen möglich. Variablen vom Typ Object unterliegen Regeln, die auf Voraussetzungen basieren, die erst ab Kapitel 4 erörtert werden.
Mit diesen Regeln werden die folgenden Zuweisungen korrekt implizit konvertiert:
| Dim x As Byte = 45, y As Single |
| y = x |
| Dim a As Short = 2, b As Decimal |
| b = a |
| Dim c As Char = CChar("A"), str As String |
| str = c |
Die implizite Konvertierung spielt immer dann eine Rolle, wenn die Typumwandlung eine Aufweitung zur Folge hat. Tritt bei einer Operation jedoch eine Einengung des Typs auf, muss explizit konvertiert werden. Das folgende Codefragment würde also zu einer Fehlermeldung führen, da ein Long mit seiner Größe von 8 Byte doppelt so viel Speicher in Anspruch nimmt wie ein nur vier Byte großer Integer.
| Dim lngVar As Long = 20 |
| Dim intVar As Integer |
| 'ACHTUNG : Unzulässige Operation |
| intVar = lngVar |
Um explizit zu konvertieren, haben Sie unter VB 2005 zwei Möglichkeiten:
| Sie rufen eine sprachspezifische Konvertierungsfunktion von VB 2005 auf (siehe Tabelle 3.5). Diese gibt es in den anderen .NET-konformen Sprachen nicht. |
| Sie nutzen die Methoden der Klasse Convert der .NET-Klassenbibliothek. | <